﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Net;

namespace NetworkingLibrary.Clients.Specialised
{
    public partial class GameServerClient : IClient
    {
        Combined.UDPTCPCombinedClient TheCombinedClient = null;
        
        public event Events.ClientConnectDelegate OnConnect = delegate { };
        public event Events.ClientDisconnectDelegate OnDisconnect = delegate { };
        public event Events.MessageReceivedDelegate OnMessageReceived = delegate { };
        public event Events.ErrorDelegate OnError = delegate { };

        public int MaxFlowRate
        {
            get
            {
                return TheCombinedClient.MaxFlowRate;
            }
            set
            {
                TheCombinedClient.MaxFlowRate = value;
            }
        }
        public DateTime LastSendTime
        {
            get
            {
                return TheCombinedClient.LastSendTime;
            }
            set
            {
                TheCombinedClient.LastSendTime = value;
            }
        }
        public bool FlowControl
        {
            get
            {
                return TheCombinedClient.FlowControl;
            }
            set
            {
                TheCombinedClient.FlowControl = value;
            }
        }
        
        public Guid ID
        {
            get
            {
                return TheCombinedClient.ID;
            }
            set
            {
                TheCombinedClient.ID = value;
            }
        }
        public int BufferSize
        {
            get;
            set;
        }
        public bool Terminating
        {
            get;
            set;
        }
        public bool Connected
        {
            get
            {
                return TheCombinedClient.Connected;
            }
        }
        
        public int MulticastTTL = 1;

        public GameServerClient(int HostTCPPort, int HostUDPPort, System.Net.IPAddress HostTCPAddress, System.Net.IPAddress UDPMulticastAddress, bool EnableRead)
            : base()
        {
            TheCombinedClient = new Combined.UDPTCPCombinedClient(HostUDPPort);
            Init();

            if (!Connect(HostTCPPort, HostUDPPort, HostTCPAddress, UDPMulticastAddress, EnableRead))
            {
                throw new Exceptions.ClientConnectFailException();
            }
        }
        public GameServerClient(System.Net.IPAddress ManagerServerAddress, int ManagerServerPort, int GameServerPortTCP, int GameServerPortUDP, bool EnableRead)
            : base()
        {
            ManagerServerClient MSClient = new ManagerServerClient(ManagerServerAddress, ManagerServerPort, true);
            if (MSClient.Connected)
            {
                TheCombinedClient = new Combined.UDPTCPCombinedClient(GameServerPortUDP);
                Init();

                Dictionary<IPAddress, IPAddress> GameServerAddresses = MSClient.GetHostAddresses();
                if (GameServerAddresses.Count > 0)
                {
                    KeyValuePair<IPAddress, IPAddress> GameServerAddress = GameServerAddresses.First();
                    if (!Connect(GameServerPortTCP, GameServerPortUDP, GameServerAddress.Key, GameServerAddress.Value, EnableRead))
                    {
                        throw new Exceptions.ClientConnectFailException();
                    }
                    else
                    {
                        MSClient.Disconnect();
                    }
                }
                else
                {
                    throw new NullReferenceException("No game servers!");
                }
            }
            else
            {
                throw new Exceptions.ClientConnectFailException();
            }
        }

        private void Init()
        {
            TheCombinedClient.OnError += new Events.ErrorDelegate(TheCombinedClient_OnError);
            
            TheCombinedClient.OnConnectTCP += new Events.ClientConnectDelegate(TheCombinedClient_OnConnectTCP);
            TheCombinedClient.OnDisconnectTCP += new Events.ClientDisconnectDelegate(TheCombinedClient_OnDisconnectTCP);
            TheCombinedClient.OnMessageReceived += new Events.MessageReceivedDelegate(TheCombinedClient_OnMessageReceived);
        }

        private void TheCombinedClient_OnError(Events.ErrorEventArgs e)
        {
            OnError.Invoke(e);
        }
        
        private void TheCombinedClient_OnConnectTCP(Events.ClientConnectEventArgs e)
        {
            OnConnect.Invoke(e);
        }
        private void TheCombinedClient_OnDisconnectTCP(Events.ClientDisconnectEventArgs e)
        {
            OnDisconnect.Invoke(e);
        }
        private void TheCombinedClient_OnMessageReceived(Events.MessageReceivedEventArgs e)
        {
            OnMessageReceived.Invoke(e);
        }

        public bool Connect(int Port, IPAddress TheAddress, bool ShouldStart = true)
        {
            return TheCombinedClient.Connect(Port, TheAddress, ShouldStart);
        }
        public bool Connect(int HostTCPPort, int HostUDPPort, IPAddress HostAddress, IPAddress UDPMulticastAddress, bool EnableRead)
        {
            return TheCombinedClient.ConnectBoth(HostTCPPort, HostAddress, HostUDPPort, UDPMulticastAddress, EnableRead, true, MulticastTTL);
        }
        public bool Disconnect(bool IgnoreSocketState = false)
        {
            return TheCombinedClient.Disconnect(IgnoreSocketState);
        }

        public bool Start()
        {
            return TheCombinedClient.Start();
        }
        public bool StartBoth()
        {
            return TheCombinedClient.StartBoth(MulticastTTL);
        }
        public bool Stop()
        {
            return TheCombinedClient.Stop();
        }

        public bool Send(byte[] Data)
        {
            return TheCombinedClient.Send(Data);
        }
        public bool Send(Messages.MessageObject TheMessage)
        {
            return TheCombinedClient.Send(TheMessage);
        }
        public bool SendUDP(byte[] Data)
        {
            return TheCombinedClient.Send(Data, true);
        }
        public bool SendUDP(Messages.MessageObject TheMessage)
        {
            return TheCombinedClient.Send(TheMessage, true);
        }
        
        public Messages.MessageObject GetMessageResponse(Messages.MessageObject MessageToSend, Type WantedResponseType)
        {
            return TheCombinedClient.GetMessageResponse(MessageToSend, WantedResponseType);
        }
    }
}
